#include "ad_process.h"
#include "ad_link.h"
#include "sklcc_api.h"

#include "ipq_main.h"
#include "ipq_api.h"

#include <stdlib.h>
#include <time.h>

static u32 call_output_random();
static u32 store_new_flow(u32 protocol,
					const struct iphdr *ad_iph,
					uint16_t ad_ipsize,uint16_t ad_rawsize);
static u32 store_old_flow(u32 protocol,
					const struct iphdr *ad_iph,
					uint16_t ad_ipsize,uint16_t ad_rawsize);

struct in_addr addr_src;
struct in_addr addr_dst;
u16 addr_src_port;
u16 addr_dst_port;



struct flow_link *head_flow = NULL;

extern u32 ad_init()
{
	//do init
	
	return 0;
}

extern u32 ad_process(u16 ad_flow_flag,struct osdpi_flow *flow,u32 protocol,const struct iphdr *ad_iph,uint16_t ad_ipsize,uint16_t ad_rawsize)
{
	//do advance detect
	addr_src.s_addr = flow->key.ip.ipv4.lower_ip;
	addr_dst.s_addr = flow->key.ip.ipv4.upper_ip;
	addr_src_port = ntohs(flow->key.lower_port);
	addr_dst_port = ntohs(flow->key.upper_port);
		
	if(ad_flow_flag == 1){
		store_new_flow(protocol,ad_iph,ad_ipsize,ad_rawsize);
	}
	if(ad_flow_flag == 0){
		store_old_flow(protocol,ad_iph,ad_ipsize,ad_rawsize);
	}

	//and,if necessary,call output_process
	call_output_random();

	return 0;
}

extern u32 ad_flow_get(struct osdpi_flow *flow,struct flow_link **return_flow)
{	
	struct flow_link *get_flow = head_flow;
	while(get_flow != NULL){
		if (get_flow->src_ip == flow->key.ip.ipv4.lower_ip
			&& get_flow->dst_ip == flow->key.ip.ipv4.upper_ip
			&& get_flow->src_port == ntohs(flow->key.lower_port)
			&& get_flow->dst_port == ntohs(flow->key.upper_port)){
			*return_flow = get_flow;	
			get_flow->get_flag = 1;
			return 0;
		}
		get_flow = get_flow->next;
	}

	return 0;
}

extern u32 ad_packet_free()
{
	struct flow_link *pre_flow = head_flow;
	while(pre_flow != NULL){
		if(pre_flow->get_flag == 1){
			while(pre_flow->head_packet != NULL){
			struct packet_link *temp_packet = pre_flow->head_packet;
			pre_flow->head_packet = pre_flow->head_packet->next;
			free(temp_packet->iph);
			free(temp_packet);
			}
			pre_flow->begin = time(NULL);
			pre_flow->protocol = 0;
			pre_flow->get_flag = 0;
			pre_flow->packet_count = 0;
			return 0;
		}else{
			pre_flow = pre_flow->next;
		}
	}
		return 0;
}

extern u32 ad_flow_free(u64 *unknow_count,u64 *unknow_count_byte)
{
	time_t nowtime = time(NULL);
	u64 count = 0;
	u64 count_byte = 0;
	struct flow_link *current_flow = head_flow;
	struct flow_link *temp_flow = NULL;
	
	while(current_flow != NULL){
		if(current_flow->protocol == 0 && (difftime(nowtime,current_flow->begin)> 300 || current_flow->packet_count >= 10)){
			while(current_flow->head_packet != NULL){
				count++;
				count_byte += current_flow->head_packet->rawsize;
				struct packet_link *temp_packet = current_flow->head_packet;
				current_flow->head_packet = current_flow->head_packet->next;
				free(temp_packet->iph);
				free(temp_packet);
			}
			if(current_flow == head_flow && current_flow->next == NULL){
				//free(head_flow);
				//head_flow = NULL;
				current_flow = NULL;
			}else if(current_flow == head_flow && current_flow->next != NULL){
				temp_flow = current_flow;
				current_flow = current_flow->next;
				current_flow->pre = NULL;
				head_flow = head_flow->next;
				free(temp_flow);	
			}else if(current_flow->next == NULL && current_flow != head_flow){
				//temp_flow = current_flow;
				//current_flow->pre->next = NULL;
				current_flow = current_flow->next;
				//free(temp_flow);
			}else{					
				temp_flow = current_flow;
				current_flow->pre->next = current_flow->next;
				current_flow->next->pre = current_flow->pre;
				current_flow = current_flow->next;
				free(temp_flow);
			}

		}else{
		current_flow = current_flow->next;}
	}
	*unknow_count = count;
	*unknow_count_byte = count_byte;

	return 0;
}




static u32 store_new_flow(u32 protocol,
					const struct iphdr *ad_iph,
					uint16_t ad_ipsize,uint16_t ad_rawsize)
{
	struct flow_link *current_flow = calloc(1,sizeof(struct flow_link));
	current_flow->head_packet = calloc(1,sizeof (struct packet_link));
	current_flow->head_packet->iph = calloc(1,ad_ipsize);
	memcpy(current_flow->head_packet->iph,ad_iph,ad_ipsize);
	current_flow->head_packet->ipsize = ad_ipsize;
	current_flow->head_packet->rawsize = ad_rawsize;

	current_flow->head_packet->next = NULL;
	current_flow->packet_count = 1;

	current_flow->src_ip = addr_src.s_addr;
	current_flow->dst_ip = addr_dst.s_addr;
	current_flow->src_port = addr_src_port;
	current_flow->dst_port = addr_dst_port;
	current_flow-> protocol = protocol;
	current_flow->begin = time(NULL);

	if (current_flow->protocol == 0){
		current_flow->head_packet->packet_flag = 0;
	}else{
		current_flow->head_packet->packet_flag = 1;
	}	

	if (head_flow == NULL){
		current_flow->pre = NULL;
		current_flow->next = NULL;
		head_flow = current_flow;
	}else{
		struct flow_link *temp_flow = head_flow;
		while(temp_flow->next != NULL){
			temp_flow = temp_flow->next;
		}
		temp_flow->next = current_flow;
		current_flow->pre = temp_flow;
		current_flow->next = NULL;
	}
	return 0;
}

static u32 store_old_flow(u32 protocol,
					const struct iphdr *ad_iph,
					uint16_t ad_ipsize,uint16_t ad_rawsize)
{
	struct flow_link *temp_flow = head_flow;
	while (temp_flow != NULL){
		if (temp_flow->next == NULL&&
			(temp_flow->src_ip != addr_src.s_addr
			|| temp_flow->dst_ip != addr_dst.s_addr
			|| temp_flow->src_port != addr_src_port
			|| temp_flow->dst_port != addr_dst_port)){
			store_new_flow(protocol,ad_iph,ad_ipsize,ad_rawsize);
			return 0;
		}	
		else if(temp_flow->src_ip == addr_src.s_addr
			&& temp_flow->dst_ip == addr_dst.s_addr
			&& temp_flow->src_port == addr_src_port
			&& temp_flow->dst_port == addr_dst_port){
			if(temp_flow->head_packet == NULL){
				temp_flow->head_packet  = calloc(1,sizeof(struct packet_link));
				temp_flow->head_packet->iph = calloc(1,ad_ipsize);
				memcpy(temp_flow->head_packet->iph,ad_iph,ad_ipsize);
				temp_flow->head_packet->ipsize = ad_ipsize;
				temp_flow->head_packet->rawsize = ad_rawsize;
				
				temp_flow->head_packet->next = NULL;
				temp_flow->protocol = protocol;
				temp_flow->packet_count = 1;
				//temp_flow->begin = time(NULL);
				if(temp_flow->protocol == 0){
					temp_flow->head_packet->packet_flag = 0;
				}else{
					temp_flow->head_packet->packet_flag = 1;
				}
			}else{
				struct packet_link *temp_packet = temp_flow->head_packet;
				while(temp_packet->next != NULL){
					temp_packet = temp_packet->next;
				}
				temp_packet->next = calloc(1,sizeof(struct packet_link));
				temp_packet->next->iph = calloc(1,ad_ipsize);
				memcpy(temp_packet->next->iph,ad_iph,ad_ipsize);
				temp_packet->next->ipsize = ad_ipsize ;
				temp_packet->next->rawsize = ad_rawsize ;

				temp_packet->next->next =NULL;
				temp_flow->protocol = protocol;
				temp_flow->packet_count++;
				if(temp_flow->protocol == 0){
					temp_packet->next->packet_flag = 0;
				}else{
					temp_packet->next->packet_flag = 1;
				}
			}
		return 0;
		} 
		temp_flow = temp_flow->next;
	}
	return 0;
}

static u32 call_output_random()
{
	srand((int)time(0));

	int random_number = rand();

	if(random_number % 2 == 0)
	{
		//do output
		PRINT_AUTHOR_INFO("output_process","10s");	
		output_process();
	}

	return 0;
}
